/** @file    PlayerPhononBundle.cpp
 *  @author  Alessandro Polo
 *  @version $Id: PlayerPhononBundle.cpp 2887 2010-08-11 08:09:41Z alex $
 *  @brief
 * File containing methods for the wosh::devices::PlayerPhononBundle class.
 * The header for this class can be found in PlayerPhononBundle.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "PlayerPhononBundle.h"
 #include "PlayerPhonon.h"

 #include <framework/entertainment/EntertainmentEvents.h>

 #include <core/ObjectAllocator.h>
 #include <core/MethodsCommon.h>
 #include <core/MessageFilter.h>
 #include <core/MessageProcessingCommon.h>
 #include <core/Fact.h>
 #include <core/List.h>
 #include <core/Table.h>
 #include <core/DataModels.h>
 #include <core/ThreadManager.h>
 #include <core/DeviceManager.h>
 #include <core/WoshKernel.h>
 #include <core/SystemError.h>


using namespace std;
namespace wosh {
 namespace devices {

 WOSH_REGISTER(wosh::devices::PlayerPhononBundle, "wosh::interfaces::devices::PlayerAudio", _PlayerPhonon_VERSION, _static_PlayerPhononBundle1 )
 WOSH_REGISTER(wosh::devices::PlayerPhononBundle, "wosh::BundleGeneric", _PlayerPhonon_VERSION, _static_PlayerPhononBundle2 )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

PlayerPhononBundle::PlayerPhononBundle( const std::string& name, const std::string& device_name )
	: BundleGeneric() {
	if ( name.size() > 0 )	BundleGeneric::setName( name, false );
	else					BundleGeneric::setName( _PlayerPhonon_NAME, false );
	Properties.update( _KEY_Version, _PlayerPhonon_VERSION );

//Log.setLevel(LOG_VERBOSE);

	this->cPlaying = -1;
	Log(LOG_DEBUG, " Configuring Player worker.." );
	this->playerWorker = new PlayerPhonon( this->Log );
	this->playerWorker->setPlayerListener(this);

	this->devState = wosh::interfaces::Device::UNKNOWN;
//	this->playerWorker->setDeviceOutput(device_name);

	Log(LOG_DEBUG, " Setting default properties and permissions.." );
	Properties.set( _Player_KEY_Volume,				40,			Permission::RW_RW_R__() );
	Properties.set( _Player_KEY_DeviceOutput,		device_name,Permission::RW_RW_R__() );
	Properties.set( _Player_KEY_CurrentFile,		"",			Permission( Permission::Read ) );
	Properties.set( _Player_KEY_Mute, 				false,		Permission( Permission::Read ) );
	Properties.set( _Player_KEY_CurrentLength,		"",			Permission( Permission::Read ) );
	Properties.set( _Player_KEY_CurrentPosition,	"",			Permission( Permission::Read ) );
	Properties.set( _Player_KEY_State,				"Idle",		Permission( Permission::Read ) );
	Properties.set( _Player_KEY_RepeatPlayList,		false,	Permission::RW_RW_R__() );
	Properties.set( _Player_KEY_RepeatItem,			false,	Permission::RW_RW_R__() );
	Properties.set( _Device_KEY_DeviceStatus,		wosh::interfaces::Device::getDeviceStateAsString(wosh::interfaces::Device::UNKNOWN), Permission( Permission::Read ) );
	Properties.monitor(_Player_KEY_Volume);
	Properties.monitor(_Player_KEY_DeviceOutput);
	Properties.monitor(_Player_KEY_RepeatItem);
	Properties.monitor(_Player_KEY_RepeatPlayList);

	Log(LOG_DEBUG, " Registering methods.." );
	MethodWRESULT* mmPause = Methods.create<MethodWRESULT>( _Player_METHOD_pause, "pause media", Permission::R_XR_XR__() );
	mmPause->setMethod( this, (MethodWRESULTPtr)&PlayerPhononBundle::mmDoPause ); //semi-direct method
	MethodWRESULT* mmStop = Methods.create<MethodWRESULT>( _Player_METHOD_stop, "stop media", Permission::R_XR_XR__() );
	mmStop->setMethod( this, (MethodWRESULTPtr)&PlayerPhononBundle::mmDoStop ); //semi-direct method
	MethodWRESULT* mmNext = Methods.create<MethodWRESULT>( _Player_METHOD_next, "play next item", Permission::R_XR_XR__() );
	mmNext->setMethod( this, (MethodWRESULTPtr)&PlayerPhononBundle::mmDoNext ); //semi-direct method
	MethodWRESULT* mmPrev = Methods.create<MethodWRESULT>( _Player_METHOD_prev, "play previous item", Permission::R_XR_XR__() );
	mmPrev->setMethod( this, (MethodWRESULTPtr)&PlayerPhononBundle::mmDoPrev ); //semi-direct method
	MethodWRESULT* mmClear = Methods.create<MethodWRESULT>( _Player_METHOD_clear, "clear playlist", Permission::R_XR_XR__() );
	mmClear->setMethod( this, (MethodWRESULTPtr)&PlayerPhononBundle::mmDoClear ); //semi-direct method
	MethodWRESULT* mmFirst = Methods.create<MethodWRESULT>( _Player_METHOD_first, "play first item", Permission::R_XR_XR__() );
	mmFirst->setMethod( this, (MethodWRESULTPtr)&PlayerPhononBundle::mmDoFirst ); //semi-direct method
	MethodWRESULT* mmLast = Methods.create<MethodWRESULT>( _Player_METHOD_last, "play last item", Permission::R_XR_XR__() );
	mmLast->setMethod( this, (MethodWRESULTPtr)&PlayerPhononBundle::mmDoLast ); //semi-direct method

	MethodList* mmList = Methods.create<MethodList>( _METHOD_List, "List enqueued items", Permission::R_XR_XR__() );
	mmList->setDataFieldExtractor( DataFieldExtractor<MediaTrack>::createFor< DataAdapterListT<MediaTrack> >(this->playerWorker->getPlayList().getTracks(), "Media Tracks") );

	MethodRetrieve* mmRetrieve = Methods.create<MethodRetrieve>( _METHOD_Retrieve, "Retrieve enqueued item(s)", Permission::R_XR_XR__() );
	mmRetrieve->setItemExtractor( ObjectExtractor<MediaTrack, long>::createFor< DataAdapterListT<MediaTrack> >(this->playerWorker->getPlayList().getTracks(), &MediaTrack::getIndex ) );

	MethodRequest* mmGetMediaList = Methods.create<MethodRequest>( _Player_METHOD_get_medialist, "Retrieve current MediaList", Permission::R_XR_XR__() );
	mmGetMediaList->setMethod( this, (MethodRequestPtr)&PlayerPhononBundle::mmDoGetMediaList );

	MethodRequest* mmPlay = Methods.create<MethodRequest>( _Player_METHOD_play, "play media", Permission::R_XR_XR__() );
	mmPlay->setMethod( this, (MethodRequestPtr)&PlayerPhononBundle::mmDoPlay );

	MethodRequest* mmPlayIndex = Methods.create<MethodRequest>( _Player_METHOD_play_index, "play item", Permission::R_XR_XR__() );
	mmPlayIndex->setMethod( this, (MethodRequestPtr)&PlayerPhononBundle::mmDoPlayIndex );

	MethodRequest* mmEnqueue = Methods.create<MethodRequest>( _Player_METHOD_enqueue, "enqueue item", Permission::R_XR_XR__() );
	mmEnqueue->setMethod( this, (MethodRequestPtr)&PlayerPhononBundle::mmDoEnqueue );

	MethodRequest* mmRemove = Methods.create<MethodRequest>( _Player_METHOD_remove, "remove an item from the list", Permission::R_XR_XR__() );
	mmRemove->setMethod( this, (MethodRequestPtr)&PlayerPhononBundle::mmDoRemove );

	MethodRequest* mmSetVolume = Methods.create<MethodRequest>( _Player_METHOD_set_volume, "set volume", Permission::R_XR_XR__() );
	mmSetVolume->setMethod( this, (MethodRequestPtr)&PlayerPhononBundle::mmDoSetVolume );

	MethodRequest* mmSetMute = Methods.create<MethodRequest>( _Player_METHOD_set_mute, "(un)set muted", Permission::R_XR_XR__() );
	mmSetMute->setMethod( this, (MethodRequestPtr)&PlayerPhononBundle::mmDoSetMute );

	MethodRequest* mmSetPosition = Methods.create<MethodRequest>( _Player_METHOD_set_position, "set media position", Permission::R_XR_XR__() );
	mmSetPosition->setMethod( this, (MethodRequestPtr)&PlayerPhononBundle::mmDoSetPosition );

	MethodRequest* mmSynchWithPlayer = Methods.create<MethodRequest>( _Player_METHOD_synch, "synchronize with player", Permission::R_XR_XR__() );
	mmSynchWithPlayer->setMethod( this, (MethodRequestPtr)&PlayerPhononBundle::mmDoSynchWithPlayer );

	setBundleState(Bundle::STATE_CREATED, false);
 }

PlayerPhononBundle::~PlayerPhononBundle() {
	Log(LOG_VERBOSE, " Destroying.." );
	if ( isBundleRunning() ) {
		Log(LOG_WARNING, "~PlayerPhononBundle() : Destroying while Bundle is running! Trying to stop.." );
		bundleStop();
	 }
	if ( this->playerWorker->isRunning() ) {
		Log(LOG_WARNING, "~PlayerPhononBundle() : Destroying while RUNNING! Trying to stop.." );
		this->playerWorker->quit();
		this->playerWorker->wait(30000);
	 }
	delete this->playerWorker; this->playerWorker = NULL;
	Log(LOG_VERBOSE, ":~PlayerPhononBundle() : Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE CONTROL

WRESULT PlayerPhononBundle::bundleStart() {
	if ( !BundleGeneric::bundleValidate_StartStop(Bundle::STATE_STARTING) ) return WRET_ERR_WRONG_STATE;

	WRESULT ret = WRET_ERR_INTERNAL;
	this->playerWorker->start();
	int MAX = 300; int i = 0;
	while( i++ < MAX ) {
		if ( this->playerWorker->isRunning() ) {
			ret = WRET_OK;
			break;
		 }
		ThreadImpl::sleepForMSec(100);
	 }

	BusDevices.setMessageHandler(this);
	WRESULT bus_connected = BusDevices.connect( _Bus_Devices, Bus::ACTION_DEFERRED );
	if ( WFAILED(bus_connected) )
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "bundleStart", "FAILED#%d Connecting Bus ("_Bus_Devices")..", bus_connected );

	if ( WSUCCEEDED(ret) ) {
		Log(LOG_VERBOSE, ":bundleStart() : Registering as Device.." );
		wosh::Device* myself = new wosh::Device(*this);
		myself->getInterfaces() = this->Interfaces;
		WoshKernel::getInstance()->devices().registerDevice( myself );

		setBundleState( Bundle::STATE_STARTED );
		Properties.set( _Device_KEY_DeviceStatus, wosh::interfaces::Device::getDeviceStateAsString(wosh::interfaces::Device::LINKED) );
	 }
	updateProperties();
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT PlayerPhononBundle::bundleStop() {
	if ( !BundleGeneric::bundleValidate_StartStop(Bundle::STATE_STOPPING) ) return WRET_ERR_WRONG_STATE;

	WRESULT ret = WRET_OK;
	if ( this->playerWorker->isPlaying() )
		ret += this->playerWorker->stop();
	ret += this->playerWorker->clear();

	this->playerWorker->quit();
	bool stopped = this->playerWorker->wait(30000);
	if ( !stopped )
		ret = WRET_ERR_INTERNAL;

	if ( WSUCCEEDED(ret) ) {
		Log(LOG_VERBOSE, ":bundleStart() : UnRegistering as Device.." );
		WoshKernel::getInstance()->devices().unRegisterDevice( this->getName() );

		setBundleState( Bundle::STATE_STOPPED );
		Properties.set( _Device_KEY_DeviceStatus, wosh::interfaces::Device::getDeviceStateAsString(wosh::interfaces::Device::UNLINKED) );
	 }
	// BUNDLE-STATE (STOPPED) will be updated async by WORKER, through call: IThreadListener::thread_event()
	return ret;
 }

WRESULT PlayerPhononBundle::bundleDiagnostic() {
	Log(LOG_VERBOSE, ":bundleDiagnostic() : Starting PlayerPhononBundle-Diagnostic" );

	WRESULT ret = BundleGeneric::bundleDiagnostic();

	Log(LOG_INFO, ":bundleDiagnostic() : Finished PlayerPhononBundle-Diagnostic [%d]", ret );
	return ret;
 }

////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE CONTROL
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// BUS MESSAGES

void PlayerPhononBundle::busMessage( const Message& message, const Bus* source ) {
	DeviceManager::busMessageForBundleDevices( message, source, this, this->Methods, this->Log, this->BusCore, this->BusDevices );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// BUS MESSAGES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENTS

bool PlayerPhononBundle::readingProperty( Property* property_curr, const PropertiesProvider* ) {
	if ( property_curr == NULL ) return true;
	if ( property_curr->getKey() == _Player_KEY_CurrentPosition ) {
		property_curr->getValue().set( this->playerWorker->getPositionMs(), Variant::TIMESPAN );
	 }
	return true;
 }

bool PlayerPhononBundle::updatingProperty( bool& do_update, const Variant& value_proposed, Property& property_current, const PropertiesProvider* source ) {
	if ( property_current.getKey() == _Player_KEY_Volume ) {
		string volumeStr = value_proposed.toString();

		if (volumeStr.size() == 0)
			return false;

		// DEADLOCK ERRROR, CHECK?
		//int volume = Utilities::fromString<int>(volumeStr);
		//WRESULT ret = this->playerWorker->setVolume(volume);
		//return WSUCCEEDED(ret);
		return false;
	 }
	else if ( property_current.getKey() == _Player_KEY_DeviceOutput ) {
		WRESULT ret = this->playerWorker->setDeviceOutput(value_proposed.toString());
		return WSUCCEEDED(ret);
	 }
	else if ( property_current.getKey() == _Player_KEY_RepeatItem ) {
		this->playerWorker->setRepeatItem(value_proposed.toBoolean(false));
		return true;
	 }
	else if ( property_current.getKey() == _Player_KEY_RepeatPlayList ) {
		this->playerWorker->setRepeatPlayList(value_proposed.toBoolean(false));
		return true;
	 }
	else // forward processing to base class
		return BundleGeneric::updatingProperty(do_update, value_proposed, property_current, source);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void PlayerPhononBundle::updateProperties() {
	string status = "";
	if ( this->playerWorker->isPlaying() )
		status = "Playing";
	else
		status = "Idle";
	Properties.transactionBeginWrite();
	Properties.update(_Player_KEY_State, status);
	Properties.update(_Player_KEY_Mute, this->playerWorker->isMuted());
	Properties.update(_Player_KEY_Volume, this->playerWorker->getVolume());
	Properties.transactionEnd();
 }

///////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// THREAD EVENTS

void PlayerPhononBundle::thread_event( Thread::THREAD_STATE thread_event, Thread* thread_source ) {
	long id = 0;
	if ( thread_source != NULL ) id = thread_source->getThreadID();
	Log(LOG_DEBUG, ":thread_event(%ld) : %s", id, Thread::getThreadStateAsString(thread_event) );
	switch(thread_event) {
		case Thread::STATE_RUNNING: {
				setBundleState( Bundle::STATE_STARTED );
				Properties.set( _Device_KEY_DeviceStatus, wosh::interfaces::Device::getDeviceStateAsString(wosh::interfaces::Device::LINKED) );
				break;
			 }
		case Thread::STATE_STOPPED: {
				setBundleState( Bundle::STATE_STOPPED );
				Properties.set( _Device_KEY_DeviceStatus, wosh::interfaces::Device::getDeviceStateAsString(wosh::interfaces::Device::UNLINKED) );
				break;
			 }
		default: break;
	 }
 }

/////////////////////////////////////////////////////////////////////////////////////////////// THREAD EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////// GENERATED EVENTS

void PlayerPhononBundle::raiseEvent( MessageContent* fact ) {
	Message* msg_event = new Message(fact);
	msg_event->setDestinationBroadcast();
	signMessage(msg_event);
	BusDevices.postMessage(msg_event);
 }

//////////////////////////////////////////////////////////////////////////////////////////// GENERATED EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// PLAYER EVENTS

void PlayerPhononBundle::changed_PlayState( int playing, PlayerPhonon* ) {
	string status = PlayerAudioEvent_StateChanged::getPlayStateAsString(playing);
	Properties.transactionBeginWrite();
	Properties.update( _Player_KEY_State, status );
	Properties.transactionEnd();
	if ( this->cPlaying != playing ) // status changed
		raiseEvent( new PlayerAudioEvent_StateChanged(playing, this->playerWorker->getVolume(), this->playerWorker->isMuted()) );
	this->cPlaying = playing;
 }

void PlayerPhononBundle::changed_Volume( int volume, bool muted, PlayerPhonon* ) {
	//Log(LOG_CRITICAL, ":changed_Volume() MAYBE DEADLOCK HERE" );
	Properties.transactionBeginWrite();
	Properties.update(_Player_KEY_Mute, muted);
	Properties.update(_Player_KEY_Volume, volume);
	Properties.transactionEnd();
	raiseEvent( new PlayerAudioEvent_StateChanged(this->playerWorker->isPlaying(), volume, muted) );
 }

void PlayerPhononBundle::changed_PlayList( const MediaList* list, PlayerPhonon* ) {
	raiseEvent( new PlayerAudioEvent_MediaListChanged(list) );
 }

void PlayerPhononBundle::changed_PlayItem( const MediaTrack* track, PlayerPhonon* ) {
	Properties.transactionBeginWrite();
	if ( track == NULL ) {
		Properties.update( _Player_KEY_CurrentFile, "" );
		Properties.update( _Player_KEY_CurrentLength, "" );
		Properties.update( _Player_KEY_CurrentPosition, "" );
	 }
	else {
		Properties.update( _Player_KEY_CurrentFile, track->getTrack().getPathFull() );
		Properties.update( _Player_KEY_CurrentLength, track->getProperties().getValueOf(_MediaTrack_KEY_Length) );
	 }
	Properties.transactionEnd();
	raiseEvent( new PlayerAudioEvent_MediaTrackChanged(track) );
 }

/////////////////////////////////////////////////////////////////////////////////////////////// PLAYER EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// PLAYER CONTROL

WRESULT PlayerPhononBundle::play()				{ return this->playerWorker->play(); }
WRESULT PlayerPhononBundle::pause()				{ return this->playerWorker->pause(); }
WRESULT PlayerPhononBundle::stop()				{ return this->playerWorker->stop(); }
WRESULT PlayerPhononBundle::clear()				{ return this->playerWorker->clear(); }
WRESULT PlayerPhononBundle::first()				{ return this->playerWorker->first(); }
WRESULT PlayerPhononBundle::next()				{ return this->playerWorker->next(); }
WRESULT PlayerPhononBundle::prev()				{ return this->playerWorker->prev(); }
WRESULT PlayerPhononBundle::last()				{ return this->playerWorker->last(); }

int PlayerPhononBundle::getVolume()				{ return this->playerWorker->getVolume(); }
bool PlayerPhononBundle::isMuted()				{ return this->playerWorker->isMuted(); }
bool PlayerPhononBundle::isPlaying()			{ return this->playerWorker->isPlaying(); }
int PlayerPhononBundle::getPlaylistCount()		{ return this->playerWorker->getPlayList().size(); }

WRESULT PlayerPhononBundle::play( unsigned int index )
												{ return this->playerWorker->play(index); }
WRESULT PlayerPhononBundle::play( const std::string& file_url )
												{ return this->playerWorker->play(file_url); }
WRESULT PlayerPhononBundle::enqueue( const std::string& file_url )
												{ return this->playerWorker->enqueue(file_url); }

WRESULT PlayerPhononBundle::remove( unsigned int index )
												{ return this->playerWorker->remove(index); }

void PlayerPhononBundle::setRepeatItem( bool value )
												{ return this->playerWorker->setRepeatItem(value); }
void PlayerPhononBundle::setRepeatPlayList( bool value )
												{ return this->playerWorker->setRepeatPlayList(value); }

WRESULT PlayerPhononBundle::setDeviceOutput( const string& dev_file )
												{ return this->playerWorker->setDeviceOutput(dev_file); }
WRESULT PlayerPhononBundle::setVolume( int newVolume ) {
	WRESULT ret = this->playerWorker->setVolume(newVolume);
	if ( WSUCCEEDED(ret) )
		updateProperties();
	return ret;
 }

WRESULT PlayerPhononBundle::setVolumeOffset( int volume_span ) {
	WRESULT ret = this->playerWorker->setVolumeOffset(volume_span);
	if ( WSUCCEEDED(ret) )
		updateProperties();
	return ret;
 }

WRESULT PlayerPhononBundle::setMute( bool on ) {
	WRESULT ret = this->playerWorker->setMute(on);
	if ( WSUCCEEDED(ret) )
		updateProperties();
	return ret;
 }

WRESULT PlayerPhononBundle::setPosition( float position_rate )
												{ return this->playerWorker->setPosition(position_rate); }
WRESULT PlayerPhononBundle::setPositionOffset( float pos_off )
												{ return this->playerWorker->setPositionOffset(pos_off); }

////////////////////////////////////////////////////////////////////////////////////////////// PLAYER CONTROL
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// OTHER METHODS

WRESULT PlayerPhononBundle::synchWithPlayer( const string& device_player_name, int flags ) {
	(void)flags;
	MediaList* ml_copy = this->playerWorker->getPlayList().clone();
	ml_copy->setCurrentPosition( this->playerWorker->getPositionMs() );

	Message* message = new Message();
	Request* msg_req = new Request( _Player_METHOD_synch, ml_copy );
	message->setSource(this);
	message->setDestinationBroadcast();
	message->getDestination().setName(device_player_name);
	message->setContent( msg_req );

	BusDevices.postMessage( message );
	return WRET_OK;
 }

WRESULT PlayerPhononBundle::synchFromPlayList( const MediaList* medialist ) {
	this->playerWorker->getPlayList() = *medialist;
	if ( !medialist->isIndexInRange() )
		return WRET_OK;
	this->playerWorker->play( medialist->getCurrentIndex() );
	if ( medialist->getCurrentPosition() > 0 )
		this->playerWorker->setPositionMs( medialist->getCurrentPosition() );
	return WRET_OK;
 }

/////////////////////////////////////////////////////////////////////////////////////////////// OTHER METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS

Response* PlayerPhononBundle::mmDoGetMediaList( const Request* request ) {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoList(%s)", option.c_str() );

	MediaList* listCopy = this->playerWorker->getPlayList().clone();
	return request->replyResponse(WRET_OK, listCopy );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* PlayerPhononBundle::mmDoEnqueue( const Request* request ) {
	if ( request == NULL ) return NULL;
	string fileStr = request->getArgument().toString();
	Log(LOG_INFO, ":mmDoEnqueue(%s)", fileStr.c_str() );
	WRESULT ret = enqueue(fileStr);
	return request->replyResponse(ret, new Variant(this->playerWorker->getPlayList().size()));
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* PlayerPhononBundle::mmDoRemove( const Request* request ) {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().toString();
	int index = Utilities::fromString<int>(option);
	Log(LOG_VERBOSE, ":mmDoRemove(%d)", index );

	WRESULT ret = remove( (unsigned int)index );
	return request->replyResponse(ret, new Variant(index));
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* PlayerPhononBundle::mmDoPlayIndex( const Request* request ) {
	if ( request == NULL ) return NULL;
	int index = request->getArgument().toInteger(-1);
	Log(LOG_VERBOSE, ":mmDoPlayIndex(%d)", index );

	WRESULT ret = play(index);
	return request->replyResponse(ret, new Variant(index));
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* PlayerPhononBundle::mmDoPlay( const Request* request ) {
	if ( request == NULL ) return NULL;
	string fileStr = request->getArgument().toString();
	Log(LOG_INFO, ":mmDoPlay(%s)", fileStr.c_str() );
	WRESULT ret = play(fileStr);
	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* PlayerPhononBundle::mmDoSynchWithPlayer( const Request* request ) {
	if ( request == NULL ) return NULL;
	WRESULT ret = WRET_OK;
	if ( !request->hasData() ) return NULL;
	if ( request->getData()->isKindOf<MediaList>() ) {
		const MediaList* medialist = request->getData()->as<MediaList>();
		if ( medialist == NULL )
			return request->replyResponse(WRET_ERR_PARAM);

		Log(LOG_INFO, ":mmDoSynchWithPlayer(%s) %d items, play %d @ %ld", "TODO", medialist->size(),
																				medialist->getCurrentIndex(),
																				medialist->getCurrentPosition()  );
		ret = synchFromPlayList(medialist);
	 }
	else if ( request->getData()->isKindOf<Variant>() ) {
		string device = request->getArgument().toString();
		if (device.size() == 0 )
			return request->replyResponse(WRET_ERR_PARAM);
		Log(LOG_INFO, ":mmDoSynchWithPlayer(%s)", device.c_str() );
		ret = synchWithPlayer(device);
	 }
	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* PlayerPhononBundle::mmDoSetVolume( const Request* request ) {
	if ( request == NULL ) return NULL;
	string volumeStr = request->getArgument().toString();
	Utilities::trimSpaces(volumeStr);
	Log(LOG_INFO, ":mmDoSetVolume(%s)", volumeStr.c_str() );
	if ( volumeStr.size() == 0 )
		return request->replyResponse(WRET_ERR_PARAM);
	WRESULT ret = WRET_OK;

	if (volumeStr.substr(0,1) == "+" || volumeStr.substr(0,1) == "-") {
		int sign = 1;
		if ( volumeStr.substr(0,1) == "-" )
			sign = -1;

		int vol_span = 2;
		if ( volumeStr.size() > 1 ) {
			vol_span = Utilities::fromString<int>(volumeStr.substr(1));
		 }
		if ( vol_span <= 0 )
			vol_span = 2;
		ret = setVolumeOffset( vol_span * sign );
	 }
	else
		ret = setVolume( Utilities::fromString<int>(volumeStr) );

	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* PlayerPhononBundle::mmDoSetMute( const Request* request ) {
	if ( request == NULL ) return NULL;
	if ( request->getArgument().isEmpty() )
		return request->replyResponse(WRET_ERR_PARAM, "Invalid Param [bool]");
	bool mute = request->getArgument().toBoolean(false);
	Log(LOG_INFO, ":mmDoSetMute(%d)", mute );
	WRESULT ret = setMute( mute );
	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* PlayerPhononBundle::mmDoSetPosition( const Request* request ) {
	if ( request == NULL ) return NULL;
	string posStr = request->getArgument().toString();
	Utilities::trimSpaces(posStr);
	Log(LOG_INFO, ":mmDoSetPosition(%s)", posStr.c_str() );

	WRESULT ret = WRET_OK;
	float pos = -1.0;
	if ( posStr == "+" )
		ret = setPositionOffset(0.1F);
	else if ( posStr == "-" )
		ret = setPositionOffset(-0.1F);
	else {
		pos = Utilities::fromString<float>(posStr, -1.0);
		ret = setPosition(pos);
	 }
	return request->replyResponse(ret);
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace devices
}; // namespace wosh
